package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.*;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Activate(group = {CommonConstants.CONSUMER,CommonConstants.PROVIDER})
public class DubboInvokeFilter   implements Filter {

    private static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
    private static List<MethodInfo> methodList =new ArrayList<MethodInfo>();

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long   startTime  = System.currentTimeMillis();
        String  methodname=invocation.getMethodName();
        try {
            // 执行方法
            return  invoker.invoke(invocation);
        } finally {
            MethodInfo method=new MethodInfo();
            long   endTime  = System.currentTimeMillis();
            method.setName(methodname);
            method.setMethodTime(endTime-startTime);
            method.setStartTime(startTime);
            method.setEndTime(endTime);
            methodList.add(method);
        }
    }

    //利用静态代码块去去调用统计方法
    static{
        statisticalTask();
    }

    /*
    * 使用定时线程池，每隔5秒统计一次TP90和TP99
    */
    private static void statisticalTask() {
        scheduledPool.scheduleWithFixedDelay(DubboInvokeFilter::doStatisticalMethod, 0, 5, TimeUnit.SECONDS);
    }

    private static void doStatisticalMethod(){
        if (methodList==null||methodList.size() == 0) {
            System.out.println("暂时没有数据需要统计");
            return;
        }
        //1.获取当前时间戳
        long currentTimeMillis = System.currentTimeMillis();
        //1.1 前一分钟的时间戳
        long oneMinuteTimeMill = currentTimeMillis - 60000;

        List<MethodInfo> timeList = methodList.stream().filter( method -> method.getStartTime()>= oneMinuteTimeMill && method.getEndTime() <= currentTimeMillis).collect(Collectors.toList());

        if (timeList.size() == 0) {
            System.out.println("一分钟之内暂时没有数据需要统计");
            return;
        }
        /* 3.根据方法名分组 */
        Map<String, List<MethodInfo>> map = timeList.stream().collect(Collectors.groupingBy(MethodInfo::getName));
        //3.1获取方法A的信息
        List<MethodInfo> sayHelloAList = map.get("methodA");
        if (!CollectionUtils.isEmpty(sayHelloAList)) {
            List<MethodInfo> sortSayHelloAList = sayHelloAList.stream().sorted(Comparator.comparing(MethodInfo::getMethodTime)).collect(Collectors.toList());
            getTpInfo(0.9, sortSayHelloAList);
            getTpInfo(0.99, sortSayHelloAList);
        }
        //3.2获取方法B的信息
        List<MethodInfo> sayHelloBList = map.get("methodB");
        if (!CollectionUtils.isEmpty(sayHelloBList)) {
            List<MethodInfo> sortSayHelloBList = sayHelloBList.stream().sorted(Comparator.comparing(MethodInfo::getMethodTime)).collect(Collectors.toList());
            getTpInfo(0.9, sortSayHelloBList);
            getTpInfo(0.99, sortSayHelloBList);
        }
        //3.3获取方法C的信息
        List<MethodInfo> sayHelloCList = map.get("methodC");
        if (!CollectionUtils.isEmpty(sayHelloCList)) {
            List<MethodInfo> sortSayHelloCList = sayHelloCList.stream().sorted(Comparator.comparing(MethodInfo::getMethodTime)).collect(Collectors.toList());
            getTpInfo(0.9, sortSayHelloCList);
            getTpInfo(0.99, sortSayHelloCList);
        }
    }

    private static void getTpInfo(double ratio, List<MethodInfo> methodTimes) {
        int size = methodTimes.size();
        int ceil = (int) Math.ceil(size * ratio);
        MethodInfo methodTime = methodTimes.get(ceil - 1);
        System.out.println("方法：" + methodTime.getName() + "的TP" + (int) (ratio * 100)
                + "为=====================>>" + methodTime.getMethodTime() + "毫秒");
    }
}
